// Copyright 2022 The Molego Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package translate

import (
	"strings"

	utils "gitee.com/west0207/molego/core/utils"
)

// dbms-name: MySQL, TiDB, MariaDB, PostgreSQL,
// 	Kingbase, Dameng, Oracle, SQLite, MsSQLServer
const (
	MySQL       = "mysql"
	TiDB        = "tidb"
	MariaDB     = "mariadb"
	PostgreSQL  = "postgres"
	Kingbase    = "kingbase"
	Dameng      = "dm"
	Oracle      = "oracle"
	SQLite      = "sqlite"
	MsSQLServer = "mssql"
)

// switch *dbmsName {
// case MySQL, MariaDB:
// case PostgreSQL, Kingbase:
// case Dameng, Oracle:
// case SQLite:
// default:
// }

//// examples
//
// 【utils.EMPTY】
// "bit":        utils.EMPTY,
// bit(6) --> bit(6)
// Bit(6) --> Bit(6)
// BIT(6) --> BIT(6)
//
// 【*】
// "number":     "decimal*",
// number(6,2) --> decimal(6,2)
// Number(8,2) --> decimal(8,2)
// NUMBER(9,2) --> decimal(9,2)
//
// 【varchar(36)】
// "uuid":       "varchar(36)",
// uuid --> varchar(36)
// UUID --> varchar(36)
//
// 【bytea】
// "binary":     "bytea",
// binary(36) --> bytea
// BINARY(36) --> bytea

// molego Type : MySQL Type
var mapMySQL = map[string]string{
	"boolean":    utils.EMPTY,
	"bit":        utils.EMPTY,
	"tinyint":    utils.EMPTY,
	"smallint":   utils.EMPTY,
	"mediumint":  utils.EMPTY,
	"int":        utils.EMPTY,
	"integer":    utils.EMPTY,
	"bigint":     utils.EMPTY,
	"float":      utils.EMPTY,
	"real":       utils.EMPTY,
	"double":     utils.EMPTY,
	"decimal":    utils.EMPTY,
	"numeric":    utils.EMPTY,
	"number":     "decimal*",
	"char":       utils.EMPTY,
	"varchar":    utils.EMPTY,
	"binary":     utils.EMPTY,
	"varbinary":  utils.EMPTY,
	"tinyblob":   utils.EMPTY,
	"blob":       utils.EMPTY,
	"mediumblob": utils.EMPTY,
	"longblob":   utils.EMPTY,
	"tinytext":   utils.EMPTY,
	"text":       utils.EMPTY,
	"mediumtext": utils.EMPTY,
	"longtext":   utils.EMPTY,
	"datetime":   utils.EMPTY,
	"timestamp":  utils.EMPTY,
	"date":       utils.EMPTY,
	"time":       utils.EMPTY,
	"uuid":       "varchar(36)",
	"currency":   "decimal(15,2)",
}

// molego Type : MsSQLServer Type
var mapMsSQLServer = map[string]string{
	"boolean":    "bit",
	"bit":        "bit",
	"tinyint":    utils.EMPTY,
	"smallint":   utils.EMPTY,
	"mediumint":  "int",
	"int":        utils.EMPTY,
	"integer":    "int",
	"bigint":     utils.EMPTY,
	"float":      "float",
	"real":       "real",
	"double":     "float",
	"decimal":    utils.EMPTY,
	"numeric":    utils.EMPTY,
	"number":     "decimal*",
	"char":       utils.EMPTY,
	"varchar":    utils.EMPTY,
	"binary":     utils.EMPTY,
	"varbinary":  utils.EMPTY,
	"tinyblob":   "image",
	"blob":       "image",
	"mediumblob": "image",
	"longblob":   "image",
	"tinytext":   "text",
	"text":       utils.EMPTY,
	"mediumtext": "text",
	"longtext":   "text",
	"datetime":   utils.EMPTY,
	"timestamp":  utils.EMPTY,
	"date":       utils.EMPTY,
	"time":       utils.EMPTY,
	"uuid":       "varchar(36)",
	"currency":   "decimal(15,2)",
}

// molego Type : PostgreSQL Type
var mapPostgreSQL = map[string]string{
	"boolean":    utils.EMPTY,
	"bit":        "bit varying*",
	"tinyint":    "smallint",
	"smallint":   utils.EMPTY,
	"mediumint":  "int",
	"int":        utils.EMPTY,
	"integer":    utils.EMPTY,
	"bigint":     utils.EMPTY,
	"float":      "float",
	"real":       "real",
	"double":     "double precision",
	"decimal":    utils.EMPTY,
	"numeric":    utils.EMPTY,
	"number":     "decimal*",
	"char":       utils.EMPTY,
	"varchar":    utils.EMPTY,
	"binary":     "bytea",
	"varbinary":  "bytea",
	"tinyblob":   "bytea",
	"blob":       "bytea",
	"mediumblob": "bytea",
	"longblob":   "bytea",
	"tinytext":   "text",
	"text":       "text",
	"mediumtext": "text",
	"longtext":   "text",
	"datetime":   "timestamp",
	"timestamp":  utils.EMPTY,
	"date":       utils.EMPTY,
	"time":       utils.EMPTY,
	"uuid":       "varchar(36)",
	"currency":   "decimal(15,2)",
}

// molego Type : Kingbase Type
// 人大金仓 V8R6+
var mapKingbase = map[string]string{
	"boolean":    utils.EMPTY,
	"bit":        "bit varying*",
	"tinyint":    "smallint",
	"smallint":   utils.EMPTY,
	"mediumint":  "int",
	"int":        utils.EMPTY,
	"integer":    utils.EMPTY,
	"bigint":     utils.EMPTY,
	"float":      "float4",
	"real":       "real",
	"double":     "double precision",
	"decimal":    utils.EMPTY,
	"numeric":    utils.EMPTY,
	"number":     "decimal*",
	"char":       utils.EMPTY,
	"varchar":    utils.EMPTY,
	"binary":     "bytea",
	"varbinary":  "bytea",
	"tinyblob":   "bytea",
	"blob":       "bytea",
	"mediumblob": "bytea",
	"longblob":   "bytea",
	"tinytext":   "text",
	"text":       "text",
	"mediumtext": "text",
	"longtext":   "text",
	"datetime":   "timestamp",
	"timestamp":  utils.EMPTY,
	"date":       utils.EMPTY,
	"time":       utils.EMPTY,
	"uuid":       "varchar(36)",
	"currency":   "decimal(15,2)",
}

// molego Type : Dameng Type
// 达梦 V8+
var mapDameng = map[string]string{
	"boolean":    "bit",
	"bit":        "bit", // 达梦库的bit类型仅支持1位，转换存在损失
	"tinyint":    utils.EMPTY,
	"smallint":   utils.EMPTY,
	"mediumint":  "int",
	"int":        utils.EMPTY,
	"integer":    utils.EMPTY,
	"bigint":     utils.EMPTY,
	"float":      "float",
	"real":       "real",
	"double":     "double",
	"decimal":    utils.EMPTY,
	"numeric":    utils.EMPTY,
	"number":     utils.EMPTY,
	"char":       utils.EMPTY,
	"varchar":    utils.EMPTY,
	"binary":     utils.EMPTY,
	"varbinary":  utils.EMPTY,
	"tinyblob":   "blob",
	"blob":       utils.EMPTY,
	"mediumblob": "blob",
	"longblob":   "blob",
	"tinytext":   "text",
	"text":       utils.EMPTY,
	"mediumtext": "text",
	"longtext":   "text",
	"datetime":   utils.EMPTY,
	"timestamp":  utils.EMPTY,
	"date":       utils.EMPTY,
	"time":       utils.EMPTY,
	"uuid":       "varchar(36)",
	"currency":   "decimal(15,2)",
}

// molego Type : Oracle Type
var mapOracle = map[string]string{
	"boolean":    "number(1,0)",
	"bit":        "number(1,0)", // oracle没有位串类型，转换存在损失
	"tinyint":    "number(3,0)",
	"smallint":   "number(5,0)",
	"mediumint":  "number(7,0)",
	"int":        "number(10,0)",
	"integer":    "number(10,0)",
	"bigint":     "number(20,0)",
	"float":      "number*",
	"real":       "number*",
	"double":     "number*",
	"decimal":    "number*",
	"numeric":    "number*",
	"number":     "number*",
	"char":       utils.EMPTY,
	"varchar":    "varchar2*",
	"binary":     "blob",
	"varbinary":  "blob",
	"tinyblob":   "blob",
	"blob":       utils.EMPTY,
	"mediumblob": "blob",
	"longblob":   "blob",
	"tinytext":   "clob",
	"text":       "clob",
	"mediumtext": "clob",
	"longtext":   "clob",
	"datetime":   "date",
	"timestamp":  utils.EMPTY,
	"date":       "date",
	"time":       "date", // oracle没有time类型，转换存在损失
	"uuid":       "varchar2(36)",
	"currency":   "number(15,2)",
}

// molego Type : SQLite Type
var mapSqlite = map[string]string{
	"boolean":    "integer",
	"bit":        "integer",
	"tinyint":    "integer",
	"smallint":   "integer",
	"mediumint":  "integer",
	"int":        "integer",
	"integer":    "integer",
	"bigint":     "integer",
	"float":      "real",
	"real":       "real",
	"double":     "real",
	"decimal":    "numeric",
	"numeric":    "numeric",
	"number":     "numeric",
	"char":       "text",
	"varchar":    "text",
	"binary":     "text",
	"varbinary":  "text",
	"tinyblob":   "blob",
	"blob":       "blob",
	"mediumblob": "blob",
	"longblob":   "blob",
	"tinytext":   "text",
	"text":       "text",
	"mediumtext": "text",
	"longtext":   "text",
	"datetime":   "real",
	"timestamp":  "real",
	"date":       "real",
	"time":       "text",
	"uuid":       "text",
	"currency":   "numeric",
}

// 获取目标数据类型字符串
// dbmsName *string 当前数据库类型
// columnType *string 字段类型，例如: varchar(36)
// return string
func GetDataType(dbmsName *string, columnType *string) string {
	key := GetColumnTypeKey(columnType)
	switch *dbmsName {
	case MySQL, MariaDB, TiDB:
		return getDbmsType(columnType, mapMySQL[key])
	case PostgreSQL:
		return getDbmsType(columnType, mapPostgreSQL[key])
	case Kingbase:
		return getDbmsType(columnType, mapKingbase[key])
	case Dameng:
		return getDbmsType(columnType, mapDameng[key])
	case Oracle:
		return getDbmsType(columnType, mapOracle[key])
	case SQLite:
		return getDbmsType(columnType, mapSqlite[key])
	case MsSQLServer:
		return getDbmsType(columnType, mapMsSQLServer[key])
	// case "h2":
	// 	return *columnType
	default:
		return *columnType
	}
}

// columnType *string 字段类型，例如: varchar(36)
// varchar(36)    --> varchar
// UUID           --> uuid
// Numeric(10, 3) --> numeric
// return string 具体类型的小写字母
func GetColumnTypeKey(columnType *string) string {
	if strings.Contains(*columnType, utils.LEFT_BRACKET) {
		// 包含了括号，有长度或精度的配置
		idx := strings.Index(*columnType, utils.LEFT_BRACKET)
		key := (*columnType)[:idx]
		return strings.ToLower(key)
	}
	return strings.ToLower(*columnType)
}

// columnType *string 字段类型，例如: varchar(36)
// mapType string Map中的对应类型值
// 例如：varchar(36), varchar2*     --> varchar2(36)
// 例如：uuid,        varchar(36)   --> varchar(36)
// 例如：char(6),     utils.EMPTY   --> char(6)
// 例如：bit,         bit varying*  --> bit varying
// return string 最终的目标类型
func getDbmsType(columnType *string, mapType string) string {
	if mapType == utils.EMPTY {
		// 为空串时原样返回
		return *columnType
	}
	if strings.HasSuffix(mapType, utils.STAR) {
		// varchar2*
		dbmsType := strings.Replace(mapType, utils.STAR, utils.EMPTY, -1)
		if strings.Contains(*columnType, utils.LEFT_BRACKET) {
			// 包含了括号，有长度或精度的配置
			idx := strings.Index(*columnType, utils.LEFT_BRACKET)
			pre := (*columnType)[idx:]
			return dbmsType + pre
		} else {
			// varchar2
			return dbmsType
		}
	}
	return mapType
}
